package org.exhaust.barbecue.processor;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.exhaust.barbecue.AbstractRequest;
import org.exhaust.barbecue.AbstractResponse;
import org.exhaust.barbecue.Action;
import org.exhaust.barbecue.ActionContext;
import org.exhaust.barbecue.action.*;
import org.exhaust.barbecue.annotation.Processor;
import org.exhaust.barbecue.spring.SpringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class MethodProcessor {

    protected Method method;

    private final static Map<Method,List<Action>> actionMap = new ConcurrentHashMap<>();

    public MethodProcessor(Method method){
        this.method = method;
    }

    public Object execute(Object handler,Object target,Object[] args) throws Exception{
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if(method.isAnnotationPresent(Processor.class)){
            Class<?> returnType = method.getReturnType();
            if (args == null || !(args[0] instanceof AbstractRequest)) {
                throw new RuntimeException("Processor注解的第一个参数必须是AbstractRequest的子类");
            }
            if ((!AbstractResponse.class.isAssignableFrom(returnType)) || returnType.isInterface() || Modifier.isAbstract(returnType.getModifiers())){
                throw new RuntimeException("返回值必须是AbstractResponse的子类");
            }
            List<Action> actionList = actionMap.computeIfAbsent(this.method,(method)->{
                Processor annotation = method.getAnnotation(Processor.class);
                List<Action> tempActionList = new LinkedList<>();
                List<Class<? extends Action>> actionClassList = new LinkedList<>();
                Class<? extends AbstractValidatorAction>[] validatorActions = annotation.validate();
                Class<? extends AbstractPrepareAction>[] prepareActions = annotation.prepare();
                Class<? extends AbstractServiceAction>[] serviceActions = annotation.service();
                Class<? extends AbstractPostAction>[] postActions = annotation.post();
                if (ArrayUtils.isEmpty(validatorActions)) {
                    actionClassList.add(ParamsAutoValidateAction.class);
                } else {
                    boolean autoFind = false;
                    for (Class<? extends AbstractValidatorAction> action : validatorActions) {
                        if (action == ParamsAutoValidateAction.class) {
                            autoFind  = true;
                        }
                        actionClassList.add(action);
                    }
                    if(!autoFind){
                        actionClassList.add(0,ParamsAutoValidateAction.class);
                    }
                }
                if (ArrayUtils.isNotEmpty(prepareActions)) {
                    for (Class<? extends AbstractPrepareAction> action : prepareActions) {
                        actionClassList.add(action);
                    }
                }
                if (ArrayUtils.isNotEmpty(serviceActions)) {
                    for (Class<? extends AbstractServiceAction> action : serviceActions) {
                        actionClassList.add(action);
                    }
                }
                if (ArrayUtils.isNotEmpty(postActions)) {
                    for (Class<? extends AbstractPostAction> action : postActions) {
                        actionClassList.add(action);
                    }
                }
                for (Class<? extends Action> actionClass : actionClassList) {
                    Action action = SpringUtils.getBean(actionClass);
                    tempActionList.add(action);
                }
                return tempActionList;
            });
            AbstractResponse response = (AbstractResponse) returnType.newInstance();
            AbstractRequest request = (AbstractRequest) args[0];
            response.setCode(200);
            response.setMessage("成功");
            ActionContext context = new ActionContext();
            context.setRequest(request);
            context.setResponse(response);
            boolean success = true;
            try {
                for (Action action : actionList) {
                    success = action.perform(request, response, context);
                    if (!success) {
                        break;
                    }
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                response.setCode(500);
                response.setMessage(e.getMessage());
                throw e;
            }
            return response;
        } else if (method.getDeclaringClass() == Object.class) {
            return method.invoke(handler, args);
        } else if ("toString".equals(method.getName()) && parameterTypes.length == 0) {
            return handler.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return handler.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return handler.equals(args[0]);
        } else if (target != null) {
            return method.invoke(target, args);
        } else {
            throw new RuntimeException("Un implemented method " + methodName);
        }
    }

}
